package com.moyun.syssecurity.sms.provider;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.moyun.model.po.system.SysUserEntity;
import com.moyun.mapper.system.SysUserMapper;
import com.moyun.syssecurity.sms.SmsCodeAuthenticationToken;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;

/**
 * <h3>app-websocket.js</h3>
 * <p>短信登陆鉴权 Provider，要求实现 AuthenticationProvider 接口</p>
 *
 * @author : ybx
 * @date : 2021-06-28 17:54:40
 **/
@RequiredArgsConstructor
@Setter
public class SmsCodeAuthenticationProvider implements AuthenticationProvider {
    private StringRedisTemplate redisTemplate;
    private SysUserMapper userMapper;

    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        SmsCodeAuthenticationToken authenticationToken = (SmsCodeAuthenticationToken) authentication;
        // 1. 校验 SmsCodeAuthenticationToken 中信息
        checkSmsCodeAuthenticationToken(authenticationToken);

        // 2. 校验通过后 根据用户手机号 查询用户,这里主要是为了封装用户的详细信息
        String mobile = (String) authenticationToken.getPrincipal();
        SysUserEntity user = userMapper.selectOne(new QueryWrapper<SysUserEntity>().eq("phone", mobile));
        user = this.userMapper.selectUserWithOrgRolesByUserId(user.getId());

        // 3. 此时鉴权成功后，应当重新 new 一个拥有鉴权的 authenticationResult 返回
        SmsCodeAuthenticationToken authenticationResult = new SmsCodeAuthenticationToken(user, user.getAuthorities());

        authenticationResult.setDetails(authenticationToken.getDetails());

        return authenticationResult;
    }

    // 重点 判断用户认证 是不是应该交由 本类处理
    @Override
    public boolean supports(Class<?> authentication) {
        // 判断 authentication 是不是 SmsCodeAuthenticationToken 的子类或子接口
        return SmsCodeAuthenticationToken.class.isAssignableFrom(authentication);
    }

    /**
     * @return void
     * @Author yangboxuan
     * @Description 校验 SmsCodeAuthenticationToken 中的信息
     * @Date 2021/8/23 16:14
     * @Param [authenticationToken]
     **/
    private void checkSmsCodeAuthenticationToken(SmsCodeAuthenticationToken authenticationToken) {
        // 1. 从 authenticationToken 中取出手机号和验证码
        String mobile = (String) authenticationToken.getPrincipal();
        String inputCode = (String) authenticationToken.getCredentials();
        // 2. 从redis中取出手机号对应的验证码
        String smsCodeRedis = this.redisTemplate.opsForValue().get(mobile);
        if (smsCodeRedis == null) {
            throw new BadCredentialsException("未检测到申请的验证码或验证码已经过期");
        }
        // 3. 将inputCode 与 smsCodeRedis 进行对比
        if (!smsCodeRedis.equals(inputCode)) {
            throw new BadCredentialsException("验证码错误");
        }
    }

}
